﻿/*
 ============================================================================
 Name        : Game2048.c
 Author      : 子墨
 Version     :
 Copyright   : copyright zimo 2018.12.22
 Description : Linux GTK Game2048 by zimo
 Introduce   : 方向键，控制节点移动，当游戏结束之后，按下除方向键的任意键，重新开始
 ============================================================================
 */


/*
 ============================================================================
 引入头文件
 ============================================================================
 */
#include <stdio.h>
#include <gtk/gtk.h>
#include <time.h> // 生成随机数
#include <gdk/gdkkeysyms.h>	// 键盘事件
#include <pthread.h> // 线程


/*
 ============================================================================
 宏定义
 ============================================================================
 */
#define WINDOW_WIDTH 500
#define WINDOW_HEIGHT 500
#define MAX_LINES 4
#define IMAGES_COUNT 13


/*
 ============================================================================
 结构体声明
 ============================================================================
 */
struct node
{
	int x; // 行索引
	int y; // 列索引
	int value; // 值
};


/*
 ============================================================================
 全局变量
 ============================================================================
 */
GtkWidget *window; // 窗体
GtkWidget *table; // 表格
GtkWidget *buttons[MAX_LINES][MAX_LINES]; // 按钮数组
struct node nodes[MAX_LINES][MAX_LINES]; // 节点数组
struct node empty_nodes[MAX_LINES * MAX_LINES]; // 空节点数组
int empty_node_count = MAX_LINES * MAX_LINES; // 空节点数量
GtkWidget *images[IMAGES_COUNT]; // images数组，由于gtk的限制，这个全局变量没用
int game_over = 0; // 游戏是否结束

/*
 ============================================================================
 功能函数
 ============================================================================
 */
// 打印nodes，测试用
void print_nodes()
{
	for(int x = 0; x < MAX_LINES; x++)
	{
		for(int y = 0; y < MAX_LINES; y++)
		{
			printf("%-4d\t",nodes[x][y].value);
		}
		printf("\n");
	}

	printf("\n\n\n\n\n");
}

// 判断node是否已满，同时将空节点存入空节点数组，记录空节点数
int is_node_full()
{
	int result = 1;
	// 每次检查都应该重置大小
	int size = 0;
	for(int x = 0; x < MAX_LINES; x++)
	{
		for(int y = 0; y < MAX_LINES; y++)
		{
			if(nodes[x][y].value == 0)
			{
				result = 0;
				// 将空节点存入数组
				empty_nodes[size].x = x;
				empty_nodes[size].y = y;
				size++;
			}
		}
	}
	// size会多加一次
	if(size > 0)
	{
		empty_node_count = size - 1;
	}
	else
	{
		empty_node_count = size;
	}
	return result;
}

// 判断游戏是否结束
int is_game_over()
{
	int result = is_node_full();
	// 按钮未装满，说明游戏没有结束
	if(result != 1)
	{
		return result;
	}
	for(int x = 0; x < MAX_LINES; x++)
	{
		for(int y = 0; y < MAX_LINES; y++)
		{
			// 上面有相同的
			if((x > 0 && nodes[x][y].value == nodes[x - 1][y].value))
			{
				result = 0;
				return result;
			}
			// 下面有相同的
			if((x < MAX_LINES - 1 && nodes[x][y].value == nodes[x + 1][y].value))
			{
				result = 0;
				return result;
			}
			// 左边有相同的
			if((y > 0 && nodes[x][y].value == nodes[x][y - 1].value))
			{
				result = 0;
				return result;
			}
			// 右边有相同的
			if((y < MAX_LINES - 1 && nodes[x][y].value == nodes[x][y + 1].value))
			{
				result = 0;
				return result;
			}
		}
	}
	// 以上情况都不满足，说明游戏结束
	result = 1;
	return result;
}

// 得到[0~range]范围内的随机数
int create_random_num(int range)
{
	if(range <= 0)
	{
		return 0;
	}
	static unsigned int seed = 0;
	seed++;
	srand((unsigned) time(NULL) + seed * seed);
	return rand() % (range + 1);
}

// 生成随机节点
void create_random_node()
{
	// nodes如果满了，退出
	if(is_node_full() == 1)
	{
		printf("%s%d\n","满了，empty_node_count = ",empty_node_count);
		return;
	}
	// nodes没满，随机拿一个空的node出来，放入一个2或4
	int random_index = create_random_num(empty_node_count - 1);
	GtkWidget *image = NULL;
	int random = create_random_num(9);
	int x = empty_nodes[random_index].x;
	int y = empty_nodes[random_index].y;
	GtkWidget *button = buttons[x][y];
	// 生成4的概率占1/10
	if(random == 0)
	{
		image = gtk_image_new_from_file("./src/images/４.png");
		gtk_button_set_image(GTK_BUTTON(button), image);

		// 设置节点的值
		nodes[x][y].x = x;
		nodes[x][y].y = y;
		nodes[x][y].value = 4;
	}
	else
	{
		image = gtk_image_new_from_file("./src/images/2.png");
		gtk_button_set_image(GTK_BUTTON(button), image);

		// 设置节点的值
		nodes[x][y].x = x;
		nodes[x][y].y = y;
		nodes[x][y].value = 2;
	}
}

// 根据图片路径file_path加载图片资源为GDKOixbuf
GdkPixbuf *create_pixbuf(const gchar * file_path)
{
    GdkPixbuf *pixbuf;
    GError *error = NULL;
    pixbuf = gdk_pixbuf_new_from_file(file_path, &error);
    if(!pixbuf)
    {
        fprintf(stderr, "%s\n", error->message);
        g_error_free(error);
    }
    return pixbuf;
}

// 左键按下
void left_press()
{
	// 标志是否合并过
	int merge = 0;
	// 遍历节点，合并
	for(int y = 0; y < MAX_LINES; y++)
	{
		for(int x = 0; x < MAX_LINES; x++)
		{
			for(int x1 = x + 1; x1 < MAX_LINES; x1++)
			{
				if (nodes[x1][y].value > 0)
				{
					if(nodes[x][y].value <= 0)
					{
						nodes[x][y].value = nodes[x1][y].value;
						nodes[x1][y].value = 0;

						x--;
						merge = 1;
					}
					else if(nodes[x][y].value == nodes[x1][y].value)
					{
						nodes[x][y].value = nodes[x][y].value * 2;
						nodes[x1][y].value = 0;

						merge = 1;
					}
					break;
				}
			}
		}
	}
	// 合并过，添加随机节点
	if(merge != 0)
	{
		create_random_node();
	}
	// 检查游戏是否结束
//	is_game_over();
}

// 右键按下
void right_press()
{
	// 标志是否合并过
	int merge = 0;
	// 遍历节点，合并
	for(int y = 0; y < MAX_LINES; y++)
	{
		for(int x = MAX_LINES - 1; x >= 0; x--)
		{
			for(int x1 = x - 1; x1 >= 0; x1--)
			{
				if (nodes[x1][y].value > 0)
				{
					if(nodes[x][y].value <= 0)
					{
						nodes[x][y].value = nodes[x1][y].value;
						nodes[x1][y].value = 0;

						x++;
						merge = 1;
					}
					else if(nodes[x][y].value == nodes[x1][y].value)
					{
						nodes[x][y].value = nodes[x][y].value * 2;
						nodes[x1][y].value = 0;

						merge = 1;
					}
					break;
				}
			}
		}
	}
	// 合并过，添加随机节点
	if(merge != 0)
	{
		create_random_node();
	}
	// 检查游戏是否结束
//	is_game_over();
}

// 上键按下
void up_press()
{
	// 标志是否合并过
	int merge = 0;
	// 遍历节点，合并
	for(int x = 0; x < MAX_LINES; x++)
	{
		for(int y = 0; y < MAX_LINES; y++)
		{
			for(int y1 = y + 1; y1 < MAX_LINES; y1++)
			{
				if (nodes[x][y1].value > 0)
				{
					if(nodes[x][y].value <= 0)
					{
						nodes[x][y].value = nodes[x][y1].value;
						nodes[x][y1].value = 0;

						y--;
						merge = 1;
					}
					else if(nodes[x][y].value == nodes[x][y1].value)
					{
						nodes[x][y].value = nodes[x][y].value * 2;
						nodes[x][y1].value = 0;

						merge = 1;
					}
					break;
				}
			}
		}
	}
	// 合并过，添加随机节点
	if(merge != 0)
	{
		create_random_node();
	}
	// 检查游戏是否结束
//	is_game_over();
}

// 下键按下
void down_press()
{
	// 标志是否合并过
	int merge = 0;
	// 遍历节点，合并
	for(int x = 0; x < MAX_LINES; x++)
	{
		for(int y = MAX_LINES - 1; y >= 0; y--)
		{
			for(int y1 = y - 1; y1 >= 0; y1--)
			{
				if (nodes[x][y1].value > 0)
				{
					if(nodes[x][y].value <= 0)
					{
						nodes[x][y].value = nodes[x][y1].value;
						nodes[x][y1].value = 0;

						y++;
						merge = 1;
					}
					else if(nodes[x][y].value == nodes[x][y1].value)
					{
						nodes[x][y].value = nodes[x][y].value * 2;
						nodes[x][y1].value = 0;

						merge = 1;
					}
					break;
				}
			}
		}
	}
	// 合并过，添加随机节点
	if(merge != 0)
	{
		create_random_node();
	}
	// 检查游戏是否结束
//	is_game_over();
}

// 刷新页面
void refresh_window()
{
	// 打印节点
//	print_nodes();

	for(int x = 0; x < MAX_LINES; x++)
	{
		for(int y = 0; y < MAX_LINES; y++)
		{
			int value = nodes[x][y].value;
			GtkWidget *image = gtk_image_new_from_file("./src/images/0.png");
//			int index = 0;
			switch(value)
			{
			case 0:
//				index = 0;
				image = gtk_image_new_from_file("./src/images/0.png");
				break;
			case 2:
//				index = 1;
				image = gtk_image_new_from_file("./src/images/2.png");
				break;
			case 4:
//				index = 2;
				image = gtk_image_new_from_file("./src/images/4.png");
				break;
			case 8:
//				index = 3;
				image = gtk_image_new_from_file("./src/images/8.png");
				break;
			case 16:
//				index = 4;
				image = gtk_image_new_from_file("./src/images/16.png");
				break;
			case 32:
//				index = 5;
				image = gtk_image_new_from_file("./src/images/32.png");
				break;
			case 64:
//				index = 6;
				image = gtk_image_new_from_file("./src/images/64.png");
				break;
			case 128:
//				index = 7;
				image = gtk_image_new_from_file("./src/images/128.png");
				break;
			case 256:
//				index = 8;
				image = gtk_image_new_from_file("./src/images/256.png");
				break;
			case 512:
//				index = 9;
				image = gtk_image_new_from_file("./src/images/512.png");
				break;
			case 1024:
//				index = 10;
				image = gtk_image_new_from_file("./src/images/1024.png");
				break;
			case 2048:
//				index = 11;
				image = gtk_image_new_from_file("./src/images/2048.png");
				break;
			case 4096:
//				index = 12;
				image = gtk_image_new_from_file("./src/images/4096.png");
				break;
			}
			// 设置图片
			gtk_button_set_image(GTK_BUTTON(buttons[x][y]), image);
//			printf("x = %d,y = %d,value = %d\n",x,y,value);
		}
	}
}

// 线程的run函数
void *thread_run()
{
	while(1)
	{
		time_t now_time;
		time(&now_time);		
		printf("%d,%s\n",now_time,"线程正在检查游戏是否结束！");
		// 强制刷新输出缓存
		fflush(stdout);
		int result = is_game_over();
		if(result == 1)
		{
			printf("%s\n","线程检查到游戏已经结束！按除方向键之外的任意键重新开始游戏");
			// 强制刷新输出缓存
			fflush(stdout);
			game_over = 1;
			pthread_exit(NULL);
//			exit(0);
		}
		sleep(1);
	}
}

// 创建线程
create_thread()
{
	// 定义检查游戏结束线程
	pthread_t thread_game_over;
	int ret = pthread_create(&thread_game_over, NULL,thread_run, NULL);
	if(ret)
	{
		printf("%s\n","线程创建失败，游戏结束！");
		// 强制刷新输出缓存
		fflush(stdout);
		exit(1);
	}
}

// 初始化表格，完成各种设置，初始化节点
void init_table()
{
	GtkWidget *button = NULL;
	GtkWidget *image = NULL;
	for(int x = 0; x < MAX_LINES; x++)
	{
		for(int y = 0; y < MAX_LINES; y++)
		{
			image = gtk_image_new_from_file("./src/images/0.png");
			struct node n = {x, y, 0};
			nodes[x][y] = n;
//			printf("random = %d\n",create_random_num(MAX_LINES - 1));
//			button = gtk_button_new_with_label("2");
			button = gtk_button_new();
//			image = gtk_image_new_from_file("./src/images/0.png");
			gtk_button_set_image(GTK_BUTTON(button), image);
			gtk_table_attach_defaults(GTK_TABLE(table), button, x, x + 1, y, y + 1);

			buttons[x][y] = button;
		}
	}
	// 随机添加两个节点
	create_random_node();
	create_random_node();

	// 初始化完成时候刷新一次页面
	refresh_window();

	// 开启线程，检查游戏是否结束
	create_thread();
}

// 键盘按下事件处理
gboolean deal_key_press(GtkWidget *widget, GdkEventKey  *event, gpointer data)
{
	int key = event->keyval;
	switch(key)
	{
		case GDK_Up:
//			printf("按了上\n");
			up_press();
			break;
		case GDK_Down:
//			printf("按了下\n");
			down_press();
			break;
		case GDK_Left:
//			printf("按了左\n");
			left_press();
			break;
		case GDK_Right:
//			printf("按了右\n");
			right_press();
			break;
		default:
			if(game_over == 1)
			{
				printf("%s\n","重新开始");
				// 强制刷新输出缓存
				fflush(stdout);
				// 移除table
				gtk_container_remove(GTK_CONTAINER(window), table);
				
				// 重新开始游戏
				table = gtk_table_new(MAX_LINES, MAX_LINES, TRUE);
				init_table();
				// 将表格加入到窗体中
				gtk_container_add(GTK_CONTAINER(window), table);

				// 显示所有控件
				gtk_widget_show_all(window);
				game_over = 0;
			}
			break;
	}
//	printf("key = %d",key);
	// 刷新页面
	refresh_window();
//	gtk_widget_show_all(widget);
	// 其他按键
//	printf("%s%d%s%c\n","key = ",key," -> ",key);
	return TRUE;
}

// 初始化窗口，完成各种设置
void init_window()
{
	// 设置窗口标题
	gtk_window_set_title(GTK_WINDOW(window), "Game2048");
	// 设置边框
	gtk_container_set_border_width(GTK_CONTAINER(window), 10);
	// 设置托盘窗口图标
	gtk_window_set_icon(GTK_WINDOW(window), create_pixbuf("./src/images/icon_2048.jpg"));

	// 设置窗口桌面的显示位置为居中
	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
	// 设置窗口的大小
	gtk_widget_set_size_request(window, WINDOW_WIDTH, WINDOW_HEIGHT);

//	// 显示所有控件
//	gtk_widget_show_all(window);

	// 绑定退出事件
	g_signal_connect(window, "destroy",G_CALLBACK(gtk_main_quit), NULL);

	// 绑定键盘事件
	g_signal_connect(window, "key-press-event",G_CALLBACK(deal_key_press), NULL);
}

// 初始化图片，因为gtk的限制，这个函数没用
void init_images()
{
	GtkWidget *image = gtk_image_new_from_file("./src/images/0.png");
	images[0] = image;

	image = gtk_image_new_from_file("./src/images/2.png");
	images[1] = image;

	image = gtk_image_new_from_file("./src/images/4.png");
	images[2] = image;

	image = gtk_image_new_from_file("./src/images/8.png");
	images[3] = image;

	image = gtk_image_new_from_file("./src/images/16.png");
	images[4] = image;

	image = gtk_image_new_from_file("./src/images/32.png");
	images[5] = image;

	image = gtk_image_new_from_file("./src/images/64.png");
	images[6] = image;

	image = gtk_image_new_from_file("./src/images/128.png");
	images[7] = image;

	image = gtk_image_new_from_file("./src/images/256.png");
	images[8] = image;

	image = gtk_image_new_from_file("./src/images/512.png");
	images[9] = image;

	image = gtk_image_new_from_file("./src/images/1024.png");
	images[10] = image;

	image = gtk_image_new_from_file("./src/images/2048.png");
	images[11] = image;

	image = gtk_image_new_from_file("./src/images/4096.png");
	images[12] = image;
}

// 主函数，程序入口
int main(int argc, char *argv[])
{
	gtk_init(&argc, &argv);

	// 创建窗口
	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	// 完成窗口初始化设置
	init_window();

	// 初始化图片
//	init_images();

	// 创建表格
	table = gtk_table_new(MAX_LINES, MAX_LINES, TRUE);
	// 初始化表格
	init_table();

	// 将表格加入到窗体中
	gtk_container_add(GTK_CONTAINER(window), table);

	// 显示所有控件
	gtk_widget_show_all(window);

	// 进入窗口程序
	gtk_main();

	return 0;
}
